using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Threading;
using DG.Common.EF.WPF;
using DG.Common.Patterns.Commands;
using DG.Common.Patterns.MVVM;
using TCC.ControleFluxoDeCaixa.Apresentacao.ViewModels.Credito;
using TCC.ControleFluxoDeCaixa.Apresentacao.Views;
using TCC.ControleFluxoDeCaixa.Model.Entities;
using TCC.ControleFluxoDeCaixa.Model.Fluxo;
using TCC.ControleFluxoDeCaixa.Model.Gerenciadores;
using TCC.ControleFluxoDeCaixa.Model.Relatorios;
using TCC.ControleFluxoDeCaixa.Model.Usuarios;

namespace TCC.ControleFluxoDeCaixa.Apresentacao.ViewModels.DashBoard
{
    public class DashBoardViewModel : ViewModelBase
    {
        public FluxoDeCaixaContextFiltrado Context = FluxoContextFactory.GetInstance().Create();
        public readonly GerenciadorDeSaldo Saldos = GerenciadorDeSaldo.GetInstance();
        private ContaResumo _selectedConta;
        private LancamentoRecorrenteResumo _selectedLancamentoRecorrente;
        private DiaFluxo _selectedDiaFluxo;
        private GerenciadorCartaoDeCreditoWindowViewModel _gerenciadorCartaoDeCreditoWindowViewModel;

        public DashBoardViewModel()
        {
            FluxoContextFactory.GetInstance().SaveChangesExecuted += (sender, time) 
                => Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
            {

                if (Context != null)
                {
                    Context.Dispose();
                }
                Context = FluxoContextFactory.GetInstance().Create();
                _contas = null;
                _lancamentosRecorrentes = null;
                _emprestimos = null;

                var extrato = SelectedExtratoMensal;
                var conta = SelectedConta;
                var dia = SelectedDiaFluxo;
                OnPropertyChanged("Contas");
                var recorrente = SelectedLancamentoRecorrente;
                OnPropertyChanged("LancamentosRecorrentes");
                if (conta != null)
                {
                    SelectedConta =
                        Contas.SingleOrDefault(c => c.Conta.ContaFinanceiraId == conta.Conta.ContaFinanceiraId);
                    if (dia != null)
                        SelectedDiaFluxo = conta.FluxoFinanceiro.DiasFluxo.Single(d => d.Dia == dia.Dia);
                }
                if (extrato != null && SelectedConta != null)
                {
                    SelectedExtratoMensal = SelectedConta.Extratos.SingleOrDefault(e => e.AnoMes == extrato.AnoMes);
                }
                if (recorrente != null)
                    SelectedLancamentoRecorrente = LancamentosRecorrentes.SingleOrDefault(c => c.Lancamento.LancamentoRecorrenteId== recorrente.Lancamento.LancamentoRecorrenteId);

                var cartao = GerenciadorCartaoDeCreditoWindowViewModel.ContaCredito;
                GerenciadorCartaoDeCreditoWindowViewModel = new GerenciadorCartaoDeCreditoWindowViewModel(Context);
                BuscarLancamentosViewModel = new BuscarLancamentoWindowViewModel(Context);

                if (cartao != null)
                    GerenciadorCartaoDeCreditoWindowViewModel.ContaCredito =
                        Context.ContasCredito.SingleOrDefault(c => c.ContaFinanceiraId == cartao.ContaFinanceiraId);

                
                var emprestimo = SelectedEmprestimo;
                OnPropertyChanged("Emprestimos");
                if (emprestimo != null)
                    SelectedEmprestimo =
                        Emprestimos.SingleOrDefault(e => e.Pessoa.PessoaId == emprestimo.Pessoa.PessoaId);

                GerenciadorCartaoDeCreditoWindowViewModel.MessageSent += SendMessage;
                BuscarLancamentosViewModel.MessageSent += SendMessage;

            }));
            GerenciadorCartaoDeCreditoWindowViewModel = new GerenciadorCartaoDeCreditoWindowViewModel(Context);
            BuscarLancamentosViewModel = new BuscarLancamentoWindowViewModel(Context);
            GerenciadorCartaoDeCreditoWindowViewModel.MessageSent += SendMessage;
            BuscarLancamentosViewModel.MessageSent += SendMessage;
        }

        private ContaResumo[] _contas;
        public ContaResumo[] Contas
        {
            get
            {
                if (_contas != null) return _contas;

                return _contas = Context.ContaFinanceiras.Except(Context.ContasCredito).ToArray().Select(c =>
                {
                    var flux = new RelatorioDeFluxoFinanceiro(Context);
                    flux.ContasFinanceiras.Add(c);
                    flux.RegerarRelatorio(comProjecao:true, incluirDiasSemVariacao:true);

                    var extratos = Context.Movimentos.Where(m => m.Conta.ContaFinanceiraId == c.ContaFinanceiraId)
                        .ToArray().GroupBy(grupo => new {Ano = grupo.DataDoMovimento.Year, Mes = grupo.DataDoMovimento.Month})
                        .Select(g =>
                        {
                                var fluxo = flux.DiasFluxo
                                            .Where(d => d.Dia.Month == g.Key.Mes
                                                && d.Dia.Year == g.Key.Ano
                                                && d.Variacao != 0).ToArray();
                            return new ContaResumoExtratoMensal(g.Key.Ano, g.Key.Mes, g.ToArray(), fluxo);
                        }).OrderBy(m=> m.Ano).ThenBy(m=> m.Mes).ToArray();
                    var saldoHoje = Saldos.CalculaSaldoDaConta(Context, c, DateTime.Now);
                    var saldoFinal = Saldos.CalculaSaldoDaConta(Context, c);
                    return new ContaResumo(extratos, c, saldoHoje, saldoFinal - saldoHoje,  flux);
                }).ToArray();
            }
        }

        public ContaResumo SelectedConta
        {
            get { return _selectedConta; }
            set
            {
                if (Equals(value, _selectedConta)) return;
                _selectedConta = value;
                OnPropertyChanged("SelectedConta");
                if (_selectedConta != null) SelectedExtratoMensal = _selectedConta.Extratos.FirstOrDefault();
            }
        }

        public ContaResumoExtratoMensal SelectedExtratoMensal
        {
            get { return _selectedExtratoMensal; }
            set
            {
                if (Equals(value, _selectedExtratoMensal)) return;
                _selectedExtratoMensal = value;
                OnPropertyChanged("SelectedExtratoMensal");
                OnPropertyChanged("MenorSaldo");
            }
        }

        public LancamentoRecorrenteResumo SelectedLancamentoRecorrente
        {
            get { return _selectedLancamentoRecorrente; }
            set
            {
                if (Equals(value, _selectedLancamentoRecorrente)) return;
                _selectedLancamentoRecorrente = value;
                OnPropertyChanged("SelectedLancamentoRecorrente");
            }
        }

        public ICommand VisualizarMovimento
        {
            get { return new FunctionalCommand((arg) =>
            {
                var mov = arg as Movimento;
                if (mov == null) return;
                
                var model = new EntityListViewerModel(() => FluxoContextFactory.GetInstance().Create(), typeof(Movimento));
                var wnd = new EntityListViewerWindow(model);
                wnd.Show();
            }, arg => arg is Movimento); }
        }

        public ICommand PagarLancamentoRecorrenteCommand
        {
            get
            {
                return new FunctionalCommand((arg) =>
                {
                    var lr = arg as LancamentoRecorrenteVencimento;
                    if (lr == null) return;

                    if (lr.Lancamento == null)
                    {
                        var model = LancamentoCadastroWindow.Inserir(null);

                        lr = model.Context.LancamentoRecorrenteVencimentos.Find(model.Context.Metadata.GetKeysFor(lr));

                        model.Entity.Nome = lr.LancamentoRecorrente.Nome + ": " + lr.Vencimento.ToShortDateString();
                        model.Entity.Categoria = lr.LancamentoRecorrente.Categoria;
                        model.Entity.DataDoLancamento = lr.Vencimento;
                        model.Movimentos.Add(new Movimento
                        {
                            Conta = lr.LancamentoRecorrente.Conta,
                            DataDoMovimento = lr.Vencimento,
                            Lancamento = model.Entity,
                            ValorAbsoluto = Math.Abs(lr.Valor),
                            TipoDeMovimento = lr.LancamentoRecorrente.TipoDeMovimento
                        });
                        model.Entity.Usuario = model.Context.GetUsuarioLogado();

                        lr.Lancamento = model.Entity;
                    }
                    else
                    {
                        LancamentoCadastroWindow.Modificar(lr.Lancamento, null);
                    }
                    
                }, arg => arg is LancamentoRecorrenteVencimento);
            }
        }

        private LancamentoRecorrenteResumo[] _lancamentosRecorrentes;
        public LancamentoRecorrenteResumo[] LancamentosRecorrentes
        {
            get
            {
                return _lancamentosRecorrentes ?? (_lancamentosRecorrentes = Context.LancamentosRecorrentes.ToArray().Select(c => new LancamentoRecorrenteResumo
                {
                    Lancamento = c
                }).ToArray());
                
            }
        }

        public DiaFluxo SelectedDiaFluxo
        {
            get { return _selectedDiaFluxo; }
            set
            {
                if (Equals(value, _selectedDiaFluxo)) return;
                _selectedDiaFluxo = value;
                OnPropertyChanged("SelectedDiaFluxo");
            }
        }

        public GerenciadorCartaoDeCreditoWindowViewModel GerenciadorCartaoDeCreditoWindowViewModel
        {
            get { return _gerenciadorCartaoDeCreditoWindowViewModel; }
            set
            {
                if (Equals(value, _gerenciadorCartaoDeCreditoWindowViewModel)) return;
                _gerenciadorCartaoDeCreditoWindowViewModel = value;
                OnPropertyChanged("GerenciadorCartaoDeCreditoWindowViewModel");
            }
        }

        private EmprestimoResumo[] _emprestimos;
        private EmprestimoResumo _selectedEmprestimo;
        private ContaResumoExtratoMensal _selectedExtratoMensal;
        private BuscarLancamentoWindowViewModel _buscarLancamentosViewModel;
        private LancamentoRecorrenteVencimento _selectedLancamentoRecorrenteVencimento;

        public EmprestimoResumo[] Emprestimos
        {
            get
            {
                return _emprestimos ?? (_emprestimos = Context.Emprestimos.GroupBy(c=> c.Pessoa).ToArray()
                    .Select(c => new EmprestimoResumo
                {
                    Pessoa = c.Key,
                    Emprestimos = c.ToArray()
                }).ToArray());

            }
        }

        public EmprestimoResumo SelectedEmprestimo
        {
            get { return _selectedEmprestimo; }
            set
            {
                if (Equals(value, _selectedEmprestimo)) return;
                _selectedEmprestimo = value;
                OnPropertyChanged("SelectedEmprestimo");
            }
        }

        public ICommand PagarEmprestimoCommand
        {
            get
            {
                return new FunctionalCommand((arg) =>
                {
                    var emp = arg as Emprestimo;
                    if (emp == null) return;

                    EmprestimoCadastroWindow.ModificarPagando(emp, null);

                }, arg => arg is Emprestimo);
            }
        }

        public BuscarLancamentoWindowViewModel BuscarLancamentosViewModel
        {
            get { return _buscarLancamentosViewModel; }
            set
            {
                if (Equals(value, _buscarLancamentosViewModel)) return;
                _buscarLancamentosViewModel = value;
                OnPropertyChanged("BuscarLancamentosViewModel");
            }
        }

        public LancamentoRecorrenteVencimento SelectedLancamentoRecorrenteVencimento
        {
            get { return _selectedLancamentoRecorrenteVencimento; }
            set
            {
                if (Equals(value, _selectedLancamentoRecorrenteVencimento)) return;
                _selectedLancamentoRecorrenteVencimento = value;
                OnPropertyChanged("SelectedLancamentoRecorrenteVencimento");
            }
        }
        
        public decimal MenorSaldo
        {
            get
            {
                return SelectedExtratoMensal != null ? Math.Min(SelectedExtratoMensal.Fluxo.Min(c => c.Saldo), 0) : 0;
            }
        }
        public decimal MenorSaldoGlobal
        {
            get
            {
                return SelectedConta != null ? Math.Min(SelectedConta.FluxoFinanceiro.MenorSaldo, 0) : 0;
            }
        }

    }

    public class EmprestimoResumo
    {
        public Pessoa Pessoa { get; set; }
        public Emprestimo[] Emprestimos { get; set; }

        public decimal Saldo
        {
            get { return -Emprestimos.Sum(e=> (int)e.TipoDeMovimento * (e.Saida.Valor - (e.Pagamento == null ? 0 : e.Pagamento.Valor))); }
        }
    }

    public class LancamentoRecorrenteResumo
    {
        public LancamentoRecorrente Lancamento { get; set; }

        public TimeSpan TempoAtraso
        {
            get
            {
                var prox = Lancamento.ProximoPagamentoEm;
                if (prox.HasValue && prox.Value < DateTime.Now.Date) return DateTime.Now.Date - prox.Value.Date;

                return TimeSpan.Zero;
            }
        }
    }

}